#ifndef PIX_HPP
#define PIX_HPP

#include <QObject>
#include <QThread>
#include <QFile>
#include <QTimer>
#include <QMutex>
#include <QMutexLocker>
#include <QApplication>

#include "mainwindow.hpp"
#include "datamemoryorganisation.hpp"
#include "cpu.hpp"
#include "decoder.hpp"
#include "stackmanagement.hpp"
#include "command.hpp"
#include "programmanagement.hpp"
#include "watchdog.hpp"
#include "hardwareinterface.hpp"

class MainWindow;
class DataMemoryOrganisation;
class HardwareInterface;
class CPU;
class Watchdog;

/**
* @brief
*
* @class Pix pix.hpp "pix.hpp"
*/
class Pix : public QThread
{
    Q_OBJECT

public:

    enum simulationModeEnum {STOP, PAUSE, RUN, TRACE, BREAK, SLEEP};

    /**
    * @brief Constructor for Pix
    *
    * @fn explicit Pix(MainWindow *parent)
    */
    explicit Pix(MainWindow *parent);

    /**
    * @brief Destructor for Pix
    *
    * @fn ~Pix()
    */
    ~Pix();

    /**
    * @brief Returns a Pointer to the ProgramManagement
    *
    * @fn ProgramManagement *getProgramManagement()
    * @return const ProgramManagement
    */
    ProgramManagement *getProgramManagement();

    /**
    * @brief Returns a Pointer to the DataMemoryOrganisation
    *
    * @fn DataMemoryOrganisation *getDataMemoryOrganisation()
    * @return const DataMemoryOrganisation
    */
    DataMemoryOrganisation *getDataMemoryOrganisation();

    /**
    * @brief Returns a Pointer to the Decoder
    *
    * @fn Decoder *getDecoder() const
    * @return Decoder
    */
    Decoder *getDecoder() const;

    /**
    * @brief Returns a Pointer to the StackManagement
    *
    * @fn StackManagement *getStackManagement() const
    * @return StackManagement
    */
    StackManagement *getStackManagement() const;

    /**
    * @brief Returns a Pointer to the CPU
    *
    * @fn CPU *getCPU() const
    * @return CPU
    */
    CPU *getCPU() const;

    /**
    * @brief Returns a Pointer to the Watchdog
    *
    * @fn Watchdog *getWatchdog() const
    * @return Watchdog
    */
    Watchdog *getWatchdog() const;

    /**
    * @brief Sets the simulationMode
    *
    * @fn void setSimulationMode(simulationModeEnum simulationMode)
    * @param simulationMode
    */
    void setSimulationMode(simulationModeEnum simulationMode);

    /**
    * @brief Resets all elements to the programstart.
    *
    * @fn void resetToProgramStart()
    */
    void resetToProgramStart();

    /**
    * @brief Activates or deactivates the fastmode
    *
    * @fn void setFastMode(bool state)
    * @param state
    */
    void setFastMode(bool state);

    /**
    * @brief Returns the current programCounter
    *
    * @fn int getProgramCounter() const
    * @return int
    */
    int getProgramCounter() const;

    /**
    * @brief Returns the current runtime as QString
    *
    * @fn QString getRuntimeString() const
    * @return QString
    */
    QString getRuntimeString() const;

    /**
    * @brief Returns the current watchdog time as QString
    *
    * @fn QString getWatchdogString() const
    * @return QString
    */
    QString getWatchdogString() const;

    /**
    * @brief Returns the current simulation mode
    *
    * @fn simulationModeEnum getSimulationMode() const
    * @return simulationModeEnum
    */
    simulationModeEnum getSimulationMode() const;

    /**
    * @brief Returns the HardwareInterface
    *
    * @fn HardwareInterface *getHardwareInterface
    * @return HardwareInterface
    */
    HardwareInterface *getHardwareInterface();

private:
    MainWindow *parent;
    Decoder *decoder;
    ProgramManagement *programManagement;
    DataMemoryOrganisation *dataMemoryOrganisation;
    StackManagement *stackManagement;
    Watchdog *watchdog;
    CPU *cpu;
    HardwareInterface *hardwareInterface;
    simulationModeEnum simulationMode;
    int programCounter;
    int waitInterval;
    bool fastMode;
    bool interrupt;
    bool initialized;
    QMutex *mutex;
    QTimer *timer;

    /**
    * @brief Starts the Thread
    *
    * @fn void run()
    */
    void run();

    /**
    * @brief Executes the cmommands
    *
    * @fn void startExecution()
    */
    void startExecution();

signals:
    // Pix Signals
    void programCounterChanged(int pc);
    void simulationStateChanged(bool state, int line);
    void codeFileParsed();

    // DataMemoryOrganisation Signals
    void registerChanged(Register *reg);
    void requestChangeRegister(int address, int value);
    void switchBankSignal(bool bank);

    // Decoder Signals
    void customHighlightingRule(QString pattern, int type);
    void customTooltip(QString var, QString tooltip);
    void variableFound(QString name, QString address);

    // ProgramManagement Signals
    void breakPointToggled(int line, bool add, QString asmCode, QString anchor);
    void requestBreakpoint(int line);

    // StackManagement Signals
    void stackOverflow();
    void pushStack(int adress, int level);
    void popStack();

    // HardwareInterface Signals
    void dataWritten(QString data);
    void dataRead(QString data);
    void hardwareInterfaceError(QString device, QString errorString);
    void hardwareInterfaceStateChanged(bool state);

public slots:
    void onInterrupt();
    void onRequestChangeRegister(int address, int value);
    void onChangeHardwareInterfaceState(QString name, bool state);

    void hwInterfaceManualSend();
    void hwInterfaceManualReceive(QString data);

    void onStop();
    void onPause();
    void onStart();
    void onTrace();
    /**
    * @brief Resets all values of PIX
    *
    * @fn void reset()
    */
    void reset();

private slots:
    void initialize();

    /**
    * @brief Reads the input file line by line
    * @details Creates a command object of every line
    *
    * @fn void readCodeFile(const QString filepath)
    * @param filepath
    */
    void readCodeFile(const QString filepath);

    /**
    * @brief Emits registerChanged signal
    *
    * @fn void onRegisterChanged(Register *reg)
    * @param reg
    */
    void onRegisterChanged(Register *reg);

    /**
    * @brief Act on a received MCLR from HardwareInterface
    *
    * @fn void onHardwareMCLR()
    */
    void onHardwareMCLR();

};

#endif // PIX_HPP
